package com.wimcorp.magic.cards;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;

import com.wimcorp.magic.abilities.Ability;
import com.wimcorp.magic.abilities.trigger.AbilityTrigger;
import com.wimcorp.magic.behaviour.Behaviour;
import com.wimcorp.magic.behaviour.Haste;
import com.wimcorp.magic.behaviour.SummoningSickness;
import com.wimcorp.magic.behaviour.Tapped;
import com.wimcorp.magic.behaviour.attack.DoubleStrike;
import com.wimcorp.magic.behaviour.attack.FirstStrike;
import com.wimcorp.magic.behaviour.counter.CounterBehavior;
import com.wimcorp.magic.color.Color;
import com.wimcorp.magic.combat.Target;
import com.wimcorp.magic.cost.Cost;
import com.wimcorp.magic.exception.AttackException;
import com.wimcorp.magic.exception.BlockerException;
import com.wimcorp.magic.players.Player;
import com.wimcorp.magic.spell.SpellHolder;
import com.wimcorp.magic.subtypes.SubType;
import com.wimcorp.magic.subtypes.SubTypeHolder;

@Entity
public class Creature extends PlayingCard implements Target, Permanent {

	private String type = "Creature";

	@Embedded
	private SubTypeHolder subType = new SubTypeHolder();

	@Column(name = "power")
	private String cardPower;
	@Column(name = "toughness")
	private String cardToughness;

	@Transient
	private int actualPower = 0;
	@Transient
	private int actualToughness = 0;

	@Transient
	private SpellHolder spellHolder = new SpellHolder(this);

	@Transient
	private Set<Ability> deathObservers = new HashSet<Ability>();

	protected Creature() {
	}

	public Creature(long multiverseid, String name, Set<SubType> subTypes, String description, Set<Color> colors,
			String power, String toughness, Set<Behaviour> behaviours, Set<Ability> abilities, Cost... costs) {
		super(multiverseid, name, description, colors, behaviours, abilities, costs);
		this.cardPower = power;
		this.cardToughness = toughness;
		for (SubType subtype : subTypes) {
			this.subType.addSubType(subtype);
		}
	}

	protected Creature(PlayingCard card) {
		super(card);
		this.cardPower = ((Creature) card).getCardPower();
		this.cardToughness = ((Creature) card).getCardToughness();
		for (SubType subtype : ((Creature) card).getSubType()) {
			this.subType.addSubType(subtype);
		}
	}

	public Creature copy(PlayingCard original) {
		return new Creature(original);
	}

	@Override
	public void cast() {
		super.cast();
		if (!hasBehaviour(Haste.instance())) {
			addBehaviour(SummoningSickness.instance());
		}
		resolveCreature();
	}

	public void attack(Target target) {
		getBehaviourHolder().attack(target);
	}

	public void counter(Creature creature) {
		getBehaviourHolder().counter(creature);
	}

	public int subtractLife(int power) {
		int life = getActualToughness() - power;
		if (life >= 0) {
			setActualToughness(life);
			return 0;
		} else {
			setActualToughness(0);
			return life * -1;
		}
	}

	public void resolveCreature() {
		resolvePower();
		resolveToughness();
		resolveCounters();
	}

	public void resolvePower() {
		int power = resolveCardPower();
		if (power != getActualPower()) {
			setActualPower(power);
		}
	}

	private int resolveCardPower() {
		String cardPower = getCardPower();
		if (StringUtils.isNumeric(cardPower)) {
			return new Integer(cardPower);
		} else {
			return 0;
		}
	}

	private void resolveToughness() {
		int toughness = resolveCardToughness();
		if (toughness != getActualToughness()) {
			setActualToughness(toughness);
		}
	}

	public int resolveCardToughness() {
		String cardToughness = getCardToughness();
		if (StringUtils.isNumeric(cardToughness)) {
			return new Integer(cardToughness);
		} else {
			return 0;
		}
	}

	private void resolveCounters() {
		Set<CounterBehavior> counters = getBehaviourHolder().getCounters();
		for (CounterBehavior counter : counters) {
			resolveCounter(counter);
		}
	}

	private void resolveCounter(CounterBehavior counter) {
		setActualPower(getActualPower() + counter.getPower());
		setActualToughness(getActualToughness() + counter.getToughness());
	}

	public void declareAttack(Player targetPlayer) throws AttackException {
		if (hasBehaviour(Haste.instance()) || !hasBehaviour(SummoningSickness.instance())) {
			if (!hasBehaviour(Tapped.instance())) {
				// if(!hasBehaviour(Vigilance))
				addBehaviour(Tapped.instance());
				getSummoningPlains().declareAttack(targetPlayer, this);
			} else {
				throw new AttackException("Create is tapped and can't attack");
			}
		} else {
			throw new AttackException("Create has summoningsickness and can't attack");
		}

	}

	public void declareBlock(Creature attacker) throws BlockerException {
		if (!hasBehaviour(Tapped.instance())) {
			getSummoningPlains().declareBlocker(attacker, this);
		} else {
			throw new BlockerException("Create is tapped and can't block");
		}

	}

	public void registerBlocker(Creature blocker) {
		getSummoningPlains().registerBlocker(this, blocker);
	}

	@Override
	public void unsummon() {
		removeBehaviour(Tapped.instance());
		removeBehaviour(SummoningSickness.instance());
		spellHolder.graveyard();
		super.unsummon();
	}

	public void addEnchantment(Enchantment enchantment) {
		spellHolder.addEnchantment(enchantment);
	}

	public void removeEnchantment(Enchantment enchantment) {
		spellHolder.removeEnchantment(enchantment);
	}

	public List<Enchantment> getEnchantments() {
		return spellHolder.getEnchantments();
	}

	public boolean hasEnchantment(Enchantment enchantment) {
		return spellHolder.hasEnchantment(enchantment);
	}

	public void addSorcery(Sorcery sorcery) {
		spellHolder.addSorcery(sorcery);
		
	}

	public void removeSorcery(Sorcery sorcery) {
		spellHolder.removeSorcery(sorcery);
	}

	public List<Sorcery> getSorceries() {
		return spellHolder.getSorceries();
	}

	public boolean hasSorcery(Sorcery sorcery) {
		return spellHolder.hasSorcery(sorcery);
	}
	
	
	public void addInstant(Instant instant) {
		spellHolder.addInstant(instant);
	}

	public void removeInstant(Instant instant) {
		spellHolder.removeInstant(instant);
	}

	public List<Instant> getInstants() {
		return spellHolder.getInstants();
	}

	public boolean hasInstant(Instant instant) {
		return spellHolder.hasInstant(instant);
	}

	public void addArtifact(Artifact artifact) {
		spellHolder.addArtifact(artifact);
	}

	public void removeArtifact(Artifact artifact) {
		spellHolder.removeArtifact(artifact);
	}

	public List<Artifact> getArtifacts() {
		return spellHolder.getArtifacts();
	}

	public boolean hasInstant(Artifact artifact) {
		return spellHolder.hasInstant(artifact);
	}

	public boolean hasSubType(SubType subType) {
		return this.subType.hasSubType(subType);
	}

	public boolean isAlive() {
		return getActualToughness() > 0;
	}

	public String toString() {
		StringBuilder card = new StringBuilder(super.toString());
		card.append("\n");
		if (getPlayer() == null) {
			card.append(getCardPower() + "/" + getCardToughness());
		} else {
			card.append(actualPower + "/" + actualToughness);
		}
		if (spellHolder.size() > 0) {
			card.append("	" +spellHolder);
			card.append("\n");
		}
		return card.toString();
	}

	@Override
	public String getCastDescription() {
		StringBuilder card = new StringBuilder(super.getCastDescription());
		card.append("\n");
		card.append(getCardPower() + "/" + getCardToughness());
		return card.toString();
	}

	public String getType() {
		return type;
	}

	public Set<SubType> getSubType() {
		return subType.getSubTypes();
	}

	public String getCardPower() {
		return cardPower;
	}

	public String getCardToughness() {
		return cardToughness;
	}

	public int getActualPower() {
		return actualPower;
	}

	public void setActualPower(int actualPower) {
		this.actualPower = actualPower;
	}

	public int getActualToughness() {
		return actualToughness;
	}

	public void setActualToughness(int life) {
		this.actualToughness = life;
		if (this.actualToughness <= 0) {
			for (Ability ability : deathObservers) {
				getPlayer().cast(ability);
			}
			graveyard();
		}
	}

	public void registerCreatureDamageListener(Ability ability) {
		getBehaviourHolder().registerCreatureDamageListener(ability);

	}

	public void unRegisterCreatureDamageListener(Ability ability) {
		getBehaviourHolder().unRegisterCreatureDamageListener(ability);

	}

	public void registerPlayerDamageListener(Ability ability) {
		getBehaviourHolder().registerPlayerDamageListener(ability);

	}

	public void unRegisterPlayerDamageListener(Ability ability) {
		getBehaviourHolder().unRegisterPlayerDamageListener(ability);

	}

	public void registerDeathListener(Ability ability) {
		deathObservers.add(ability);

	}

	public void unRegisterDeathListener(Ability ability) {
		deathObservers.remove(ability);

	}

	

}
